package com.cskaoyan.javase.oop3._2inner._1member._1basic;

/**
 * 在具体研究成员内部类的访问特点之前,先明确两个原则:
 *      1.成员内部类和外围类是"好兄弟",互相之间访问不受权限限制,
 *          即便是私有,也能够任意访问.
 *          但是这仅限于成员内部类和外围类之间,其他类或者外部类没有这个特点
 *
 *      2.如何理解成员内部类对象,依赖于外围类对象而存在?
 *          成员内部类对象必须在外围类对象的基础上创建
 *          思考:
 *              1.成员内部类的成员方法中,要隐含几个对象?
 *                  肯定有一个对象是自身对象,当前对象,用this指向
 *                  既然成员内部类对象已经存在了,那么外围类对象一定也存在
 *                  所以这里还有一个对象
 *                      是外围类对象,用外围类类名.this指向
 *                  所以这里总共有两个对象
 *
 *              2.外围类的成员方法中,要隐含几个对象?
 *                  只有一个对象,当前对象,自身外围类对象,用this指向
 *
 *              3.外围类的静态成员方法中,没有对象,什么对象都没有.
 *
 *              4.外围类的成员方法中,没有成员内部类对象,那么想访问成员内部类对象成员时,肯定需要创建它的对象,那么如何创建呢?
 *              直接new关键字创建,因为已经存在外围类对象了
 *
 *              5.外围类的静态成员方法中,没有成员内部类对象,也没有外围类对象,那么想访问成员内部类对象成员时,肯定需要创建它的对象,那么如何创建呢?
 *              先创建外围类对象,然后在外围类对象的基础上,再创建成员内部类对象
 *
 * 成员内部类的访问特点,就是成员内部类怎么用?
 * 成员内部类的访问特点，主要从以下几个角度研究：
 *      1.成员内部类内部访问外围类成员
 *          a.在成员内部类的成员方法中访问外围类成员
 *                  已经隐含了外围类对象
 *                  可以直接访问外围类成员
 *                  类似于访问自身成员变量时,因为隐含this,不需要把this写出来.
 *
 *                  特殊情况:
 *                      1.当成员内部类的成员变量和外围类成员变量同名时,首先要遵循就近原则优先访问自身成员
 *                      这时如果还想访问外围类成员
 *                      需要使用
 *                      "外围类类名.this"表示访问外围类成员
 *                      2.当成员内部类的全局常量和外围类同名了,用类名区分即可.
 *
 *      2.外围类访问成员内部类成员
 *          a.外围类的成员方法中访问成员内部类成员
 *              直接创建成员内部类对象即可
 *              用创建出来的对象访问成员即可
 *              不受访问权限限制,也不需要区分同名
 *
 *          b.外围类的静态成员方法中访问成员内部类成员
 *          先创建外围类对象,再创建成员内部类对象
 *          访问时,用哪个对象访问,就是谁的成员
 *          不需要区分
 *          语法:
 *          在外围类对象的基础上,创建成员内部类对象
 *          InnerClazz mic = new EnclosedClazz().new InnerClazz();
 *
 *      3.外部类访问成员内部类成员
 *          首先需要考虑访问权限
 *              需要外围类的权限
 *              还需要成员内部类权限
 *              即便是上述权限都有,能够创建成员内部类对象
 *              访问内部类成员,也需要受访问权限限制
 *
 *              在外部类访问成员内部类对象时,不管是成员方法,还是静态方法,都没有区别
 *              都要先创建外围类对象
 *              然后再创建成员内部类对象
 *
 *              语法:
 *              EnclosedClazz.InnerClazz ic = new EnclosedClazz().new InnerClazz();
 *              EnclosedClazz.InnerClazz是告诉编译器内部类的归属,属于哪个外围类
 *              在外部创建内部类对象,这是必须的
 *
 *      4.成员内部类访问外部类成员
 *          需要创建对象
 *          但是创建对象,访问成员等整个过程,都受访问权限的限制.
 *
 *
 * @since 10:50
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        EnclosedClazz ec = new EnclosedClazz();
        EnclosedClazz.InnerClazz ic = ec.new InnerClazz();

        // 上述二合一,链式调用
        EnclosedClazz.InnerClazz ic2 = new EnclosedClazz().new InnerClazz();

        System.out.println(ic.a);
        // System.out.println(ic.b);
    }

    public void test(){}
}
class Student{
    int age;
    private String name;
}
class EnclosedClazz {
    int a;
    private int b = 10;
    static int c = 20;
    static final int D = 30;

    public void testEnclosed(){
        // 访问成员内部类成员,需要先创建对象
        // 如何创建对象?直接创建
        InnerClazz ic = new InnerClazz();
        // 实际上,上述语句写完整,是这样的
        InnerClazz ic2 = this.new InnerClazz();

        System.out.println(this.a);
    }

    public static void testStatic(){
        // 先创建外围类对象,再创建成员内部类对象
        EnclosedClazz ec = new EnclosedClazz();
        InnerClazz ic = ec.new InnerClazz();
        // 以上过程可以二合一,用链式调用
        InnerClazz ic2 = new EnclosedClazz().new InnerClazz();
    }

     class InnerClazz {
        int a;
        private int b = 100;
        static final int D = 300;

        // 成员方法
        public void testInner(int b) {
            System.out.println(b);
            System.out.println(this.b);
            System.out.println(EnclosedClazz.this.b);
            System.out.println(a);
            System.out.println(EnclosedClazz.this.a);
            System.out.println(EnclosedClazz.c);
            System.out.println(EnclosedClazz.D);
            System.out.println(InnerClazz.D);

            Student s = new Student();
            System.out.println(s.age);
            // System.out.println(s.name);
        }
    }
}